<?php

class Jf_Application {

    protected $_bootstrap;

    protected $_environment;

    protected $_config = null;

    protected $_options = array();

    protected $_catchException = false;

    protected $_displayErrors = false;

    protected $_modules = array();

    protected static $_app = null;

    private function __clone() {}

    private function __sleep() {}

    private function __wakeup() {}

    /**
     * 类初始化
     * 
     * @param string $config 配置文件名称
     * @param string $environment 配置文件中的全局块标记【master/develop】
     */
    public function __construct($config, $environment = null) {
        $app = self::app();
        if (! is_null($app)) {
            throw new Jf_Exception('Only one application can be initialized');
        }
        // 配置文件中的全局块标记
        $this->_environment = $environment;
        // 配置项初始化
        $this->_config = Jf_Config::factory('ini', $config, $environment);
        // 配置项解析执行
        $this->parseOptions($this->_config);
        $request = new Jf_Controller_Request_Http();
        $this->_dispatcher = Jf_Controller_Dispatcher::getInstance();
        $this->_dispatcher->setRequest($request);
        self::$_app = $this;
    }

    /**
     * 获取配置文件中的全局块标记
     * 
     * @return string
     */
    public function getEnvironment() {
        return $this->_environment;
    }

    /**
     * 参数解析
     * 
     * @param array $options 参数集合
     * @return boolean
     */
    protected function parseOptions($options) {
        $options = $options['application'];
        if (! empty($options['catchException'])) {
            $this->_catchException = $options['catchException'];
        }
        if (! empty($options['displayErrors'])) {
            $this->_displayErrors = $options['displayErrors'];
        }
        if (! empty($options['name'])) {
            Jf_Global::set('app_name', $options['name']);
        } else {
            throw new Jf_Exception('not set application.name');
        }
        if (! empty($options['directory'])) {
            Jf_Global::set('directory', preg_replace("/" . preg_quote(DIRECTORY_SEPARATOR, "/") . "$/", "", $options['directory']));
        } else {
            throw new Jf_Exception('Expected a directory entry in application configures');
        }
        if (! empty($options['ext'])) {
            Jf_Global::set('ext', $options['ext']);
        }
        if (! empty($options['bootstrap']) && is_string($options['bootstrap'])) {
            $this->_options['bootstrap'] = $options['bootstrap'];
        }
        if (! empty($options['view']) && is_array($options['view']) && ! empty($options['view']['ext']) && is_string($options['view']['ext'])) {
            Jf_Global::set('view_ext', $options['view']['ext']);
        }
        if (! empty($options['baseUri']) && is_string($options['baseUri'])) {
            $this->_options['baseUri'] = $options['baseUri'];
        } else {
            $this->_options['baseUri'] = $_SERVER['PHP_SELF'];
        }
        if (! empty($options['dispatcher']['defaultModule'])) {
            $module = $options['dispatcher']['defaultModule'];
        } else {
            $module = Jf_Controller_Router::DEFAULT_MODULE;
        }
        Jf_Global::set('default_module', $module);
        if (! empty($options['dispatcher']['defaultController'])) {
            $controller = $options['dispatcher']['defaultController'];
        } else {
            $controller = Jf_Controller_Router::DEFAULT_CONTROLLER;
        }
        Jf_Global::set('default_controller', $controller);
        if (! empty($options['dispatcher']['defaultAction'])) {
            $action = $options['dispatcher']['defaultAction'];
        } else {
            $action = Jf_Controller_Router::DEFAULT_ACTION;
        }
        Jf_Global::set('default_action', $action);
        if (isset($options['dispatcher']['defaultRoute']) && is_array($options['dispatcher']['defaultRoute'])) {
            Jf_Global::set('default_route', $options['dispatcher']['defaultRoute']);
        }
        if (! empty($options['modules']) && is_string($options['modules'])) {
            $modules = preg_split("/,/", $options['modules']);
            foreach ($modules as $module) {
                $this->_modules[] = ucfirst(strtolower(trim($module)));
            }
        }
        if (empty($this->_modules)) {
            $this->_modules[] = Jf_Global::get('default_module');
        }
        Jf_Global::set('modules', serialize($this->_modules));
        return true;
    }

    /**
     * 获取模块名称
     * 
     * @return string
     */
    public function getModules() {
        return $this->_modules;
    }

    /**
     * 项目模块名称判断
     * 
     * @param string $module 模块名称
     * @return boolean
     */
    public static function isModuleName($module) {
        $app = self::app();
        if ($app == null) {
            return false;
        }
        $modules = $app->getModules();
        if (array_search(strtolower($module), array_map('strtolower', $modules)) !== false) {
            return true;
        }
        return false;
    }

    /**
     * 项目初始化
     */
    public static function app() {
        return self::$_app;
    }

    /**
     * 获取项目配置
     */
    public function getConfig() {
        return $this->_config;
    }

    /**
     * 引导处理，依据app目录下的Bootstrap.php文件处理
     */
    public function bootstrap() {
        $bootstrap = $this->_options['bootstrap'];
        require $bootstrap;
        $bootstrapClass = Jf_Bootstrap::DEFAULT_BOOTSTRAP;
        Jf_Bootstrap::bootstrap(new $bootstrapClass(), $this->_dispatcher);
        return $this;
    }

    /**
     * 项目启动
     */
    public function run() {
        // 时区设置
        date_default_timezone_set("PRC");
        // 错误提示打印与否处理
        if ($this->_displayErrors != 1) {
            set_error_handler(function ($errno, $errstr, $errfile, $errline) {
                Jf::log($errno . $errstr . $errfile . $errline, 3, 'error.log');
            });
        } else {
            ini_set('display_errors', 'on');
            error_reporting(E_ALL & ~ E_RECOVERABLE_ERROR & ~ E_STRICT);
        }
        // 引导处理
        $this->bootstrap();
        // 异常处理方式
        if ($this->_catchException == 1) {
            return $this->_dispatcher->dispatch();
        } else {
            try {
                return $this->_dispatcher->dispatch();
            } catch (Exception $exc) {
                Jf::log($exc->getTraceAsString(), 0, 'debug.log');
                $module = Jf_Global::get('default_module');
                $this->_dispatcher->getRequest()->setModuleName($module)->setControllerName('error')->setActionName('index')->setParams(array(
                    'traceString' => $exc->getTraceAsString()
                ));
                $this->_dispatcher->dispatch();
            }
        }
    }
}
